﻿using AutoMapper;
using IdentityModel.Client;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using Serilog.Core;
using ShanNiang.Hosting;
using ShanNiang.Hosting.Common;
using ShanNiang.Hosting.Enumerate;
using ShanNiang.Hosting.Extensions;
using ShanNiang.Service.Constants;
using ShanNiang.Service.Interfaces;
using ShanNiang.Service.LoginModels.Responses;
using System.Collections;
using System.ComponentModel.DataAnnotations;
using System.Net.Http.Json;
using System.Reflection;
using System.Text;
using System.Web;

namespace ShanNiang.Service.Implementations
{
    //[DILifetime(LifetimeType.Singleton)]
    public class ClientService<TReq> : IClientService<TReq> where TReq : RequestBase
    {
        private readonly PackingConfig _packingConfig;
        private readonly IMapper _mapper;
        private readonly ILogger _logger;
        private readonly UserContext _userContext;
        public ClientService(PackingConfig packingConfig, UserContext userContext, IMapper mapper, ILogger logger)
        {
            _packingConfig = packingConfig;
            _userContext = userContext;
            _mapper = mapper;
            _logger = logger;
            //Server = ClinetConfiguration.Xk0Server;
            HttpClientHandler = new HttpClientHandler
            {
                ServerCertificateCustomValidationCallback = (message, certificate2, arg3, arg4) =>
                {
                    return true;
                }
            };
        }
        protected DiscoveryDocumentResponse? Disco { get; set; }
        protected IdsToken? Token
        {
            get { return _userContext.Token; }
            set { _userContext.Token = value; }
        }
        //protected DateTime LastTokenTime { get; set; }
        protected HttpClientHandler HttpClientHandler { get; set; }

        //internal string Server { get; set; }
        //protected  string ServerUrl { get; }

        protected async Task ValidateToken()
        {
            if (Token == null || Token.CreatedTime.AddSeconds(Token.ExpiresIn) < DateTime.Now)
            {
                if (Disco == null)
                {
                    try
                    {
                        var client = new HttpClient(HttpClientHandler);
                        Disco = await client.GetDiscoveryDocumentAsync(_packingConfig.IdentityServer);
                        if (Disco.IsError)
                        {
                            throw new Exception("获取发现文档报错", Disco.Exception);
                        }
                    }
                    catch (Exception)
                    {
                        Disco = null;
                        throw;
                    }
                    _logger.LogDebug($"{nameof(Disco)} is {Disco?.HttpResponse?.StatusCode}");
                }
                ///if (Token == null || LastTokenTime.AddSeconds(Token.ExpiresIn) < DateTime.Now)
                //{
                await GetToken();
                //}
            }
        }
        protected async Task GetToken()
        {
            if (_userContext.WxAppLoginResponse.Success)
            {
                var client = new HttpClient(HttpClientHandler);
                //LastTokenTime = DateTime.Now;
                try
                {
                    var request = new PasswordTokenRequest
                    {
                        Address = Disco?.TokenEndpoint,
                        ClientId = _packingConfig.ClientId,
                        ClientSecret = _packingConfig.ClientSecret,
                        Scope = CommonData.XkmScopes,
                        UserName = _userContext.WxAppLoginResponse.UserName ?? "",
                        Password = "xxxxxx"
                    };
                    request.Parameters.Add("policy_name", "provider");
                    request.Parameters.Add("sign", _userContext.WxAppLoginResponse.Sign ?? "");
                    request.Parameters.Add("login_provider", _userContext.WxAppLoginResponse.LoginProvider ?? "");
                    request.Parameters.Add("provider_key", _userContext.WxAppLoginResponse.ProviderKey ?? "");


                    var token = await client.RequestPasswordTokenAsync(request);
                    if (token.IsError)
                    {
                        throw new Exception("获取Token报错", token.Exception);
                    }
                    else
                    {
                        Token = _mapper.Map<IdsToken>(token);
                        //_userContext.Token = Token;
                        _logger.LogDebug($"获取Token成功，token is {Token?.AccessToken} tokenEndpoint is {Disco?.TokenEndpoint}");
                    }
                }
                catch (Exception)
                {
                    Token = null;
                    throw;
                }
            }
        }
        /// <summary>
        /// 请求变成queryString
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="request"></param>
        /// <returns></returns>
        public static string RequestToQueryString(TReq request)
        {
            if (request == null)
            {
                return string.Empty;
            }
            var result = new StringBuilder();
            var properties = typeof(TReq).GetProperties(BindingFlags.Instance | BindingFlags.Public).OrderBy(p => p.Name).ToArray();
            foreach (var property in properties)
            {
                var ignoreAttr = property.GetCustomAttribute<JsonIgnoreAttribute>();
                if (ignoreAttr == null)
                {
                    var value = property.GetValue(request);
                    var require = property.GetCustomAttribute<RequiredAttribute>();
                    if (require != null)
                    {
                        if (value == null)
                        {
                            throw new ApiException(GlobalErrorCode.BadRequest);
                        }
                    }
                    if (value != null)
                    {
                        if (property.PropertyType != typeof(string) && property.PropertyType.GetInterface("IEnumerable") == typeof(IEnumerable))
                        {
                            var vaule = property.GetValue(request);
                            var enumerable = vaule != null ? (IEnumerable)vaule : null;
                            if (enumerable != null)
                            {
                                foreach (var item in enumerable)
                                {
                                    if (property.PropertyType.IsEnum)
                                    {
                                        result.Append(HttpUtility.UrlEncode(value == null ? "" : ((int)value).ToString()));
                                    }
                                    else
                                    {
                                        result.Append(HttpUtility.UrlEncode(value == null ? "" : value.ToString()));
                                    }
                                    result.Append('&');
                                }
                            }
                        }
                        else
                        {
                            result.Append($"{property.Name}=");
                            if (property.PropertyType.IsEnum)
                            {
                                result.Append(HttpUtility.UrlEncode(value == null ? "" : ((int)value).ToString()));
                            }
                            else
                            {
                                result.Append(HttpUtility.UrlEncode(value == null ? "" : value.ToString()));
                            }
                            result.Append('&');
                        }
                    }
                }
            }
            return result.Remove(result.Length - 1, 1).ToString();
        }
        /// <summary>
        /// 获取响应数据
        /// </summary>
        /// <typeparam name="TRsp">响应类型</typeparam>
        /// <param name="request">请求对象</param>
        /// <param name="server">服务器</param>
        /// <returns></returns>
        public async Task<TRsp?> GetResponse<TRsp>(TReq request)
        {
            var server = "";
            switch (request.ApiServer)
            {
                case ApiServer.IdsServer:
                    server = _packingConfig.IdentityServer; break;
                case ApiServer.XkmServer:
                    server = _packingConfig.XkmServer; break;
            }
            var url = request.GetUrl(server);
            _logger.LogDebug($"[Begin Request]\nRequestUrl:{request.Method}|{url}\nRequestData:{request.ToJson()}");
            TRsp? result = default;
            try
            {

                if (request.NeedToken)
                {
                    await ValidateToken();
                }
                var client = new HttpClient(HttpClientHandler);
                if (Token != null && Token.AccessToken != null)
                {
                    client.SetBearerToken(Token.AccessToken);
                }
                if (request.Method == HttpMethod.Get)
                {
                    if (request != null)
                    {
                        url = url += $"?{RequestToQueryString(request)}";
                    }
                    result = await client.GetFromJsonAsync<TRsp>(url);
                }
                else if (request.Method == HttpMethod.Delete)
                {
                    if (request != null)
                    {
                        url = url += $"?{RequestToQueryString(request)}";
                    }
                    result = await client.DeleteFromJsonAsync<TRsp>(url);
                }
                else if (request.Method == HttpMethod.Post)
                {
                    var response = await client.PostAsJsonAsync(url, request);
                    if (response != null)
                    {
                        result = await response.Content.ReadFromJsonAsync<TRsp>();
                    }
                }
                else if (request.Method == HttpMethod.Put)
                {
                    var response = await client.PutAsJsonAsync(url, request);
                    if (response != null)
                    {
                        var respStr = await response.Content.ReadAsStringAsync();
                        _logger.LogWarning(respStr);
                        result = await response.Content.ReadFromJsonAsync<TRsp>();
                    }
                }
                _logger.LogDebug($"[End Request]:\nResponseData:{result?.ToJson()};");
            }
            catch (Exception ex)
            {
                _logger.LogWarning(ex, $"Request: {url} has errors");
            }
            return result;
        }
        /// <summary>
        /// 设置token
        /// </summary>
        /// <param name="token"></param>
        internal void SetToken(IdsToken token)
        {
            Token = token;
        }
        public async Task<IdsToken?> GetTokenAsync()
        {
            if (_userContext.WxAppLoginResponse.Success)
            {
                await ValidateToken();
                return Token;
            }
            return null;
        }
    }
}
